package com.ihr360.payroll.service.salaryplan.impl;

import com.google.common.collect.Lists;
import com.ihr360.authority.sharing.valid.AuthValid;
import com.ihr360.authority.sharing.valid.EnableAuthValidation;
import com.ihr360.authority.sharing.validator.GetOneValidator;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.specification.Ihr360Specification;
import com.ihr360.entity.BasicStaffInfoEntity;
import com.ihr360.entity.BasicStaffInfoInSalaryPlanEntity;
import com.ihr360.payroll.dao.SalaryPlanRepository;
import com.ihr360.payroll.dao.SalaryPlanStaffRepository;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlanStaff;
import com.ihr360.payroll.model.dto.salary.plan.config.PayslipConfig;
import com.ihr360.payroll.model.dto.salary.plan.idclass.SalaryPlanStaffId;
import com.ihr360.payroll.service.salaryplan.SalaryPlanService;
import com.ihr360.payroll.service.salaryplan.SalaryPlanStaffService;
import com.ihr360.shared.basicstaffinfo.dto.SyncedBasicStaffInfo;
import com.ihr360.shared.basicstaffinfo.service.BasicStaffInfoService;
import com.ihr360.validation.ValidationUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SalaryPlanStaffServiceImpl implements SalaryPlanStaffService {

    @Autowired
    private SalaryPlanStaffRepository salaryPlanStaffRepository;
    @Autowired
    private SalaryPlanService salaryPlanService;
    @Autowired
    private BasicStaffInfoService basicStaffInfoService;
    @Autowired
    private SalaryPlanRepository salaryPlanRepository;

    @Override
    @EnableAuthValidation
    public List<SalaryPlanStaff> save(@AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long planId, List<SalaryPlanStaff> salaryPlanStaffs) {
        return (List<SalaryPlanStaff>) salaryPlanStaffRepository.save(salaryPlanStaffs);
    }

    @Override
    @EnableAuthValidation
    public SalaryPlanStaff save(@AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long planId, SalaryPlanStaff salaryPlanStaff) {
        return salaryPlanStaffRepository.save(salaryPlanStaff);
    }

    @Override
    @EnableAuthValidation
    public SalaryPlanStaff changePaySlipConfig(@AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long salaryPlanId, String staffId, Long paySlipConfigId) {
        SalaryPlanStaffId salaryPlanStaffId = new SalaryPlanStaffId();
        salaryPlanStaffId.setSalaryPlanId(salaryPlanId);
        salaryPlanStaffId.setStaffId(staffId);
        SalaryPlanStaff salaryPlanStaff = salaryPlanStaffRepository.findOne(salaryPlanStaffId);
        if (salaryPlanStaff == null) {
            return null;
        }
        salaryPlanStaff.setPaySlipConfigId(paySlipConfigId);
        return salaryPlanStaffRepository.save(salaryPlanStaff);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @EnableAuthValidation
    public void dummyAddStaff2Plan(@AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long planId, List<String> staffIdList) {
        ValidationUtils.rejectIfEmpty(staffIdList, null, "请指定要添加的员工");
        List<SalaryPlanStaff> existedStaffs = salaryPlanStaffRepository.findAllBySalaryPlanIdAndStaffIdIn(planId, staffIdList);
        List<SalaryPlanStaff> savedStaffs = Lists.newArrayList();
        existedStaffs.forEach(planStaff -> {
            if (planStaff.getStaffState() == SalaryPlanStaff.StaffState.TEMP_REMOVE) {
                planStaff.setStaffState(SalaryPlanStaff.StaffState.NORMAL);
                savedStaffs.add(planStaff);
                staffIdList.remove(planStaff.getStaffId());
            }
        });
        if (CollectionUtils.isNotEmpty(staffIdList)) {
            staffIdList.forEach(staffId -> savedStaffs.add(new SalaryPlanStaff(planId, staffId, true, SalaryPlanStaff.StaffState.TEMP_ADD)));
        }
        save(planId,savedStaffs);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @EnableAuthValidation
    public void deleteSalaryPlanStaff(@AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long planId, List<String> staffIdList) {
        salaryPlanStaffRepository.deleteBySalaryPlanIdAndStaffIdIn(planId, staffIdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @EnableAuthValidation
    public void dummyDeleteSalaryPlanStaff(@AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long planId, List<String> staffIdList) {
        ValidationUtils.rejectIfEmpty(staffIdList, null, "请指定要删除的员工");
        List<SalaryPlanStaff> planStaffList = salaryPlanStaffRepository.findAllBySalaryPlanIdAndStaffIdIn(planId, staffIdList);
        ValidationUtils.rejectIfEmpty(planStaffList, null, "要删除的员工不存在，请刷新后重试");

        List<SalaryPlanStaff> updatedStaffs = Lists.newArrayList();
        List<SalaryPlanStaff> deletedStaffs = Lists.newArrayList();
        planStaffList.forEach(planStaff -> {
            if (planStaff.getStaffState() == SalaryPlanStaff.StaffState.NORMAL) {
                planStaff.setStaffState(SalaryPlanStaff.StaffState.TEMP_REMOVE);
                updatedStaffs.add(planStaff);
            } else if (planStaff.getStaffState() == SalaryPlanStaff.StaffState.TEMP_ADD) {
                deletedStaffs.add(planStaff);
            }
        });

        if (CollectionUtils.isNotEmpty(updatedStaffs)) {
            save(planId,updatedStaffs);
        }
        if (CollectionUtils.isNotEmpty(deletedStaffs)) {
            salaryPlanStaffRepository.delete(deletedStaffs);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @EnableAuthValidation
    public void resetSalaryPlanStaffStatus(@AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long planId) {
        List<SalaryPlanStaff> salaryPlanStaffs = salaryPlanStaffRepository.findBySalaryPlanId(planId);
        if (CollectionUtils.isEmpty(salaryPlanStaffs)) {
            return;
        }
        List<SalaryPlanStaff> updatedStaffs = Lists.newArrayList();
        List<SalaryPlanStaff> deletedStaffs = Lists.newArrayList();
        salaryPlanStaffs.forEach(planStaff -> {
            if (planStaff.getStaffState() == SalaryPlanStaff.StaffState.TEMP_REMOVE) {
                planStaff.setStaffState(SalaryPlanStaff.StaffState.NORMAL);
                updatedStaffs.add(planStaff);
            } else if (planStaff.getStaffState() == SalaryPlanStaff.StaffState.TEMP_ADD) {
                deletedStaffs.add(planStaff);
            }
        });
        if (CollectionUtils.isNotEmpty(updatedStaffs)) {
            save(planId,updatedStaffs);
        }
        if (CollectionUtils.isNotEmpty(deletedStaffs)) {
            salaryPlanStaffRepository.delete(deletedStaffs);
        }
    }

    @Override
    @EnableAuthValidation
    public Page<BasicStaffInfoInSalaryPlanEntity> getStaffs4SalaryPlan(String companyId, @AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long planId,
                                                                       Specification<SyncedBasicStaffInfo> specification, Pageable pageable) {
        SalaryPlan salaryPlan = salaryPlanService.getOne(planId);
        Map<Long, PayslipConfig> payslipConfigMap = salaryPlan.getPayslipConfigs().stream()
                .collect(Collectors.toMap(PayslipConfig::getId, payslipConfig -> payslipConfig));
        List<SalaryPlanStaff> salaryPlanStaffList = salaryPlanStaffRepository
                .findBySalaryPlanIdAndStaffStateIn(planId,
                        Lists.newArrayList(SalaryPlanStaff.StaffState.NORMAL, SalaryPlanStaff.StaffState.TEMP_REMOVE));
        Page<BasicStaffInfoEntity> pageData = getBasicStaffInfoIn(companyId, specification, pageable, salaryPlanStaffList);
        List<BasicStaffInfoInSalaryPlanEntity> content = new ArrayList<>();
        if (CollectionUtils.isEmpty(pageData.getContent())) {
            return new PageImpl<>(content, pageable, pageData.getTotalElements());
        }
        Map<String, PayslipConfig> staffIdPaySlipConfigMap = new HashMap<>();
        for (SalaryPlanStaff salaryPlanStaff : salaryPlanStaffList) {
            if (salaryPlanStaff.getPaySlipConfigId() != null) {
                staffIdPaySlipConfigMap.put(salaryPlanStaff.getStaffId(), payslipConfigMap.get(salaryPlanStaff.getPaySlipConfigId()));
            }
        }
        pageData.getContent().forEach(basicStaffInfoEntity -> {
            BasicStaffInfoInSalaryPlanEntity basicStaffInfoInSalaryPlanEntity = new BasicStaffInfoInSalaryPlanEntity();
            BeanUtils.copyProperties(basicStaffInfoEntity, basicStaffInfoInSalaryPlanEntity);
            String staffId = basicStaffInfoEntity.getId();
            if (staffIdPaySlipConfigMap.containsKey(staffId)) {
                PayslipConfig payslipConfig = staffIdPaySlipConfigMap.get(staffId);
                if (payslipConfig != null) {
                    basicStaffInfoInSalaryPlanEntity.setPaySlipConfigId(payslipConfig.getId());
                    basicStaffInfoInSalaryPlanEntity.setPaySlipConfigName(payslipConfig.getName());
                }
            }
            content.add(basicStaffInfoInSalaryPlanEntity);
        });
        return new PageImpl<>(content, pageable, pageData.getTotalElements());
    }

    @Override
    @EnableAuthValidation
    public Page<BasicStaffInfoEntity> getStaffs4ActivePayPeriod(String companyId, @AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long planId, Ihr360Specification<SyncedBasicStaffInfo> specification, Pageable pageable) {
        List<SalaryPlanStaff> salaryPlanStaffList = salaryPlanStaffRepository
                .findBySalaryPlanIdAndStaffStateIn(planId,
                        Lists.newArrayList(SalaryPlanStaff.StaffState.NORMAL, SalaryPlanStaff.StaffState.TEMP_ADD));
        return getBasicStaffInfoIn(companyId, specification, pageable, salaryPlanStaffList);
    }

    @Override
    @EnableAuthValidation
    public List<SalaryPlanStaff> getListBySalaryPlanIdAndStaffStates(@AuthValid(validator = GetOneValidator.class, entity = SalaryPlan.class) Long salaryPlanId, List<Integer> staffStatusList) {
        return salaryPlanStaffRepository.findAllBySalaryPlanIdAndStaffStateIn(salaryPlanId, staffStatusList);
    }

    @Override
    public Map<String, List<String>> getStaffNameAndStaffIdMapByStaffIdList(List<String> staffIdList) {
        Map<String, List<String>> result = CollectionUtils.newHashMap();
        if (CollectionUtils.isEmpty(staffIdList)) {
            return result;
        }
        List<SalaryPlanStaff> salaryPlanStaffList = salaryPlanStaffRepository.findAllByStaffIdIn(staffIdList);
        if (CollectionUtils.isEmpty(salaryPlanStaffList)) {
            return result;
        }

        List<Long> planIdList = new ArrayList<>();
        salaryPlanStaffList.forEach(salaryPlanStaff -> planIdList.add(salaryPlanStaff.getSalaryPlanId()));

        List<SalaryPlan> salaryPlanList = salaryPlanRepository.findByIdIn(planIdList);
        Map<Long, String> panIdAndNameMap = salaryPlanList.stream().collect(Collectors.toMap(SalaryPlan::getId, SalaryPlan::getName));

        salaryPlanStaffList.forEach(salaryPlanStaff -> {
            Long planId = salaryPlanStaff.getSalaryPlanId();
            String staffId = salaryPlanStaff.getStaffId();
            List<String> planNames = result.get(staffId);
            if (CollectionUtils.isEmpty(planNames)) {
                planNames = new ArrayList<>();
            }
            planNames.add(panIdAndNameMap.get(planId));
            result.put(staffId, planNames);
        });
        return result;
    }

    private Page<BasicStaffInfoEntity> getBasicStaffInfoIn(String companyId, Specification<SyncedBasicStaffInfo> specification, Pageable pageable, List<SalaryPlanStaff> salaryPlanStaffList) {
        if (CollectionUtils.isEmpty(salaryPlanStaffList)) {
            return new PageImpl<>(Collections.emptyList());
        }
        List<String> staffIds = salaryPlanStaffList.stream().map(SalaryPlanStaff::getStaffId).collect(Collectors.toList());
        return basicStaffInfoService.getStaffsInclude(companyId, staffIds, specification, pageable);
    }
}
